package edu.nctu.csie.jichang.spring;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.ManagedList;

/**
 * Must consider of random order of java properties
 * 
 * Override Class   => d.class   = edu.nctu.csie.jichang.spring.bean.A
 * Remove Class     => e.class   = null
 * Reference Bean   => main.instance     = bean-ref:b
 *
 * List Replace     => main.list[rep] = a,b,c
 * List Add Item    => main.list[add] = b
 * List Remove Item => main.list[del] = c
 */
public class EnhancePropertyOverride extends EnhanceLocation {
	
	private static final String LIST_CONTENT = "[rep]";
	private static final String LIST_ADD     = "[add]";
	private static final String LIST_REMOVE  = "[del]";
	
	private static final String BEAN_REF     = "bean-ref:";
	private static final String BEAN_CLASS   = "class";
	private static final String BEAN_REMOVE  = "null";

	protected void applyPropertyValue(ConfigurableListableBeanFactory pFactory, String pBeanName, String pProperty, String pValue) {
		
		if (logger.isDebugEnabled()) {
			logger.debug("BeanName = " + pBeanName +" , Property = " + pProperty + " , Value = " + pValue);
		}

		if(pProperty.toLowerCase().endsWith(LIST_CONTENT)){
			handleListReplace(pFactory, pBeanName, pProperty, pValue);
		}else if(pProperty.toLowerCase().endsWith(LIST_ADD)){
			handleListAdd(pFactory, pBeanName, pProperty, pValue);
		}else if(pProperty.toLowerCase().endsWith(LIST_REMOVE)){
			handleListRemove(pFactory, pBeanName, pProperty, pValue);
		}else if (pValue.startsWith(BEAN_REF)) {
			handleBeanReference(pFactory, pBeanName, pProperty, pValue);
		}else if(pProperty.trim().equalsIgnoreCase(BEAN_CLASS)){
			handleBeanChange(pFactory, pBeanName, pValue);
		}else{
			super.applyPropertyValue(pFactory, pBeanName, pProperty, pValue);
		}
		
	}
	
	private void handleBeanChange(ConfigurableListableBeanFactory pFactory,String pBeanName, String pValue) {
		if (pFactory instanceof DefaultListableBeanFactory) {
			DefaultListableBeanFactory tBeanFactory = (DefaultListableBeanFactory) pFactory;
			if(pValue.trim().equalsIgnoreCase(BEAN_REMOVE)){
				tBeanFactory.removeBeanDefinition(pBeanName);
			}else{
				BeanDefinition tDefinition = getBeanDefinition(pFactory, pBeanName);
				tDefinition.setBeanClassName(pValue);
			}
		}
	}
	private void handleBeanReference(ConfigurableListableBeanFactory pFactory,String pBeanName, String pProperty, String pValue) {
		String tValue = pValue.substring(BEAN_REF.length());
		BeanDefinition tDefinition = getBeanDefinition(pFactory, pBeanName);
		tDefinition.getPropertyValues().addPropertyValue(pProperty, new RuntimeBeanReference(tValue));
	}
	private void handleListRemove(ConfigurableListableBeanFactory pFactory,String pBeanName, String pProperty, String pValue) {
		String tPropertyName = pProperty.substring(0,pProperty.indexOf(LIST_REMOVE));
		BeanDefinition tDefinition = getBeanDefinition(pFactory, pBeanName);
		if(pValue.indexOf("\"")>=0){ // 表示是字串
			handleStrListRemove(pValue, tPropertyName, tDefinition);
		}else{
			handleBeanListRemove(pValue, tPropertyName, tDefinition);
		}
	}

	private void handleBeanListRemove(String pValue, String tPropertyName,BeanDefinition tDefinition) {
		List<BeanReference> tItem = getBeanReferencePropertyList(tPropertyName, tDefinition);
		List<String> tDeleteName = getBeanNameList(pValue);
		List<BeanReference> tDelete = new ArrayList<BeanReference>();
		for (BeanReference r : tItem) {
			if(tDeleteName.contains(r.getBeanName())){
				tDelete.add(r);
			}
		}
		tItem.removeAll(tDelete);
		tDefinition.getPropertyValues().addPropertyValue(tPropertyName, tItem);
	}

	private void handleStrListRemove(String pValue, String tPropertyName,BeanDefinition tDefinition) {
		List<TypedStringValue> tItem = getTypedStringValuePropertyList(tPropertyName, tDefinition);
		pValue = pValue.substring(pValue.indexOf("\"")+1, pValue.lastIndexOf("\""));
		List<String> tDeleteName =  getBeanNameList(pValue);
		List<TypedStringValue> tDelete = new ArrayList<TypedStringValue>();
		for (TypedStringValue t : tItem) {
			if(tDeleteName.contains(t.getValue())){
				tDelete.add(t);
			}
		}
		tItem.removeAll(tDelete);
		tDefinition.getPropertyValues().addPropertyValue(tPropertyName, tItem);
	}
	private void handleListAdd(ConfigurableListableBeanFactory pFactory,String pBeanName, String pProperty, String pValue) {
		String tPropertyName = pProperty.substring(0,pProperty.indexOf(LIST_ADD));
		BeanDefinition tDefinition = getBeanDefinition(pFactory, pBeanName);
		if(pValue.indexOf("\"")>=0){ // 表示是字串
			handleStrListAdd(pValue, tPropertyName, tDefinition);
		}else{
			handleBeanListAdd(pValue, tPropertyName, tDefinition);
		}
	}

	private void handleBeanListAdd(String pValue, String tPropertyName,BeanDefinition tDefinition) {
		List<BeanReference> tItem = getBeanReferencePropertyList(tPropertyName, tDefinition);
		List<String> tAddName = getBeanNameList(pValue);
		for (String name : tAddName) {
			boolean tAdd = true;
			for (BeanReference r : (List<BeanReference>)tItem) {
				if(r.getBeanName().equals(name)){
					tAdd = false;
				}
			}
			if(tAdd){
				tItem.add(new RuntimeBeanReference(name));
			}
		}
		tDefinition.getPropertyValues().addPropertyValue(tPropertyName, tItem);
	}

	private void handleStrListAdd(String pValue, String tPropertyName,BeanDefinition tDefinition) {
		List<TypedStringValue> tItem = getTypedStringValuePropertyList(tPropertyName, tDefinition);
		pValue = pValue.substring(pValue.indexOf("\"")+1, pValue.lastIndexOf("\""));
		List<String> tAddName =  getBeanNameList(pValue);
		for (String name : tAddName) {
			tItem.add(new TypedStringValue(name));
		}
		tDefinition.getPropertyValues().addPropertyValue(tPropertyName, tItem);
	}

	private void handleListReplace(ConfigurableListableBeanFactory pFactory,String pBeanName, String pProperty, String pValue) {
		String tPropertyName = pProperty.substring(0,pProperty.indexOf(LIST_CONTENT));
		BeanDefinition tDefinition = getBeanDefinition(pFactory, pBeanName);
		if(pValue.indexOf("\"")>=0){
			handleStrListReplace(pValue, tPropertyName, tDefinition);
		}else{
			handleBeanListReplace(pValue, tPropertyName, tDefinition);
		}
	}

	private void handleBeanListReplace(String pValue, String tPropertyName,BeanDefinition tDefinition) {
		List<BeanReference> tItem = getBeanReferencePropertyList(tPropertyName, tDefinition);
		List<String> tAddName = getBeanNameList(pValue);
		tItem.clear();
		for (String name : tAddName) {
			tItem.add(new RuntimeBeanReference(name));
		}
		tDefinition.getPropertyValues().addPropertyValue(tPropertyName, tItem);
	}

	private void handleStrListReplace(String pValue, String tPropertyName,BeanDefinition tDefinition) {
		List<TypedStringValue> tItem = getTypedStringValuePropertyList(tPropertyName, tDefinition);
		pValue = pValue.substring(pValue.indexOf("\"")+1, pValue.lastIndexOf("\""));
		List<String> tAddName =  getBeanNameList(pValue);
		tItem.clear();
		for (String name : tAddName) {
			tItem.add(new TypedStringValue(name));
		}
		tDefinition.getPropertyValues().addPropertyValue(tPropertyName, tItem);
	}
	@SuppressWarnings("unchecked")
	private List<BeanReference> getBeanReferencePropertyList(String pPropertyName,BeanDefinition pDefinition) {
		MutablePropertyValues tPropertyValues = pDefinition.getPropertyValues();
		for (PropertyValue p : tPropertyValues.getPropertyValueList()) {
			if(p.getName().equals(pPropertyName)){
				if (p.getValue() instanceof ManagedList<?>) {
					return (List<BeanReference>)p.getValue();
				}
			}
		}
		return new ArrayList<BeanReference>();
	}
	@SuppressWarnings("unchecked")
	private List<TypedStringValue> getTypedStringValuePropertyList(String pPropertyName,BeanDefinition pDefinition) {
		MutablePropertyValues tPropertyValues = pDefinition.getPropertyValues();
		for (PropertyValue p : tPropertyValues.getPropertyValueList()) {
			if(p.getName().equals(pPropertyName)){
				if (p.getValue() instanceof ManagedList<?>) {
					return (List<TypedStringValue>)p.getValue();
				}
			}
		}
		return new ArrayList<TypedStringValue>();
	}
	
	private BeanDefinition getBeanDefinition(ConfigurableListableBeanFactory pFactory, String pBeanName) {
		BeanDefinition tDefinition = pFactory.getBeanDefinition(pBeanName);
		while (tDefinition.getOriginatingBeanDefinition() != null) {
			tDefinition = tDefinition.getOriginatingBeanDefinition();
		}
		return tDefinition;
	}

	private List<String> getBeanNameList(String pValue) {
		List<String> tRemoveName = new ArrayList<String>();
		if(pValue.indexOf(",")>0){
			for (String name : pValue.split(",")) {
				tRemoveName.add(name);
			}
		}else{
			tRemoveName.add(pValue);
		}
		return tRemoveName;
	}
}